import re

class TextFormatting:
	def __init__(self, command):
		words = command.upper().split()
		self.command = words[0]
		self.arguments = words[1:]
		self.negate = False
		if self.command.startswith("/"):
			self.negate = True
			self.command = self.command[1:]
	def __str__(self):
		if self.negate:
			return "TextFormatting(\"/%s\")" % self.command
		else:
			return "TextFormatting(\"%s\")" % self.command
	def __repr__(self):
		return str(self)
	def isBreak(self):
		return self.command == "BREAK"
	def isLinebreak(self):
		return self.command == "N"
	def isInstant(self):
		return self.isLinebreak()
	def modify(self, tags):
		if self.isInstant(): return
		if self.command in tags and self.negate:
			tags.remove( self.command )
			return
		if not (self.command in tags or self.negate):
			tags.append( self.command )
			return
		from rlcore import ProgrammerGoof
		raise ProgrammerGoof( "inappropriate tag modification (%s)" % self.command )
	def colourValue(self, c):
		from rlcore import Colour
		try: return {
				"RED": (255,96,96),
				"BLUE": (96,96,255),
				"GREEN": (96,255,96),
				"YELLOW": (255,255,0),
				"BLACK": (0,0,0),
				"WARNING": (255,40,40),
				"DARKBRASS": (0x59,0x26,0x00),
			}[c]
		except KeyError: return None
	def isInvisible(self):
		return self.command == "INVISIBLE"
	def fgColour(self):
		return self.colourValue( self.command )
	def bgColour(self):
		if not self.command.startswith( "BG" ): return None
		return self.colourValue( self.command[2:] )

def makeEnglishList(items):
	if len(items) < 1: return "nothing"
	if len(items) < 2: return items[0]
	return ", ".join( items[:-1] ) + " and " + items[-1]

def tagify( tag ):
	return "".join( ["<", tag, ">"] )

def parseFormattedText( parsable ):
	words = []
#	linebreaks = re.compile( r"(\n+)" )
	spaceTags = re.compile( r"(\s+)" )
	findTags = re.compile( r"(<.+?>)" )
	extractTag = re.compile( r"<(.+?)>" )
#	parsable = linebreaks.sub( tagify("BREAK")+tagify("LINEBREAK")+tagify("BREAK"), parsable )
	parsable = spaceTags.sub( tagify("BREAK"), parsable )
	rv = findTags.split( parsable )
	for i in range(len(rv)):
		m = extractTag.match( rv[i] )
		if m:
			rv[i] = TextFormatting( m.group(1) )
	return rv

class TextWrapper:
	def __init__(self, width, pageHeight, moreIndicator):
		self.width = width
		self.pageHeight = pageHeight
		self.more = moreIndicator
	def parse(self, text):
		self.pages = []
		self.currentPage = []
		self.currentLine = []
		self.tagsInEffect = []
		ss = self.breakSymbolSequence( parseFormattedText( text ) )
		while True:
			availableSpace = self.width - self.textLength( self.currentLine )
			if availableSpace != self.width: availableSpace -= 1
			if self.onelinerLength( ss ) <= availableSpace:
				while ss:
					self.feedOnelinerSymbols( ss.pop(0) )
				self.currentPage.append( self.currentLine )
				self.pages.append( self.currentPage )
				break
			assert ss
			self.feed( ss.pop(0) )
		for i in range(len(self.pages)):
			for j in range(len(self.pages[i])):
				self.pages[i][j] = self.simplify( self.pages[i][j] )
		return self.pages
	def nextPage(self):
		self.pages.append( self.currentPage )
		self.currentPage = []
	def simplify(self, line):
		rv = []
		nelt = ""
		for s in line:
			if isinstance(s,TextFormatting):
				if nelt: rv.append( nelt )
				nelt = ""
				rv.append( s )
			else:
				nelt += s
		rv.append( nelt )
		return rv
	def nextLine(self):
		if len(self.currentPage)+1 == self.pageHeight:
			for tag in self.tagsInEffect:
				self.currentLine.append( TextFormatting("/" + tag ) )
			self.currentLine += self.more
		self.currentPage.append( self.currentLine )
		self.currentLine = [ TextFormatting( cmd ) for cmd in self.tagsInEffect ]
		if len(self.currentPage) == self.pageHeight:
			self.nextPage()
	def textLength(self, nobreaksequence):
		return sum( [ len(s) for s in nobreaksequence if not isinstance(s,TextFormatting) ] )
	def onelinerLength(self, ss):
		return sum( [ self.textLength(s) for s in ss ] ) + len(ss) - 1
	def breakSymbolSequence(self, ss):
		rv = []
		crv = []
		for s in ss:
			if isinstance(s,TextFormatting) and s.isBreak():
				rv.append( crv )
				crv = []
			else:
				if s: crv.append( s )
		return rv + [crv]
	def feedOnelinerSymbols(self, symbols):
		if self.textLength( self.currentLine ) > 0:
			self.currentLine.append( " " )
		for symbol in symbols:
			if isinstance(symbol, TextFormatting):
				if symbol.isLinebreak():
					pageNotEmpty = (self.currentLine and self.textLength(self.currentLine))
					pageNotEmpty = pageNotEmpty or (self.currentPage and self.textLength( self.currentPage[0] ))
					if pageNotEmpty:
						self.nextLine()
				else:
					symbol.modify( self.tagsInEffect )
					self.currentLine.append( symbol )
			else:
				self.currentLine.append( symbol )
	def feed(self, symbols):
		# assumed that the text can NOT be completed in one line
		# assumed that symbols contains no breaks
		length = self.textLength( symbols )
		lineLength = self.textLength( self.currentLine )
		spaceAvailable = self.width - lineLength
		if len(self.currentPage)+1 == self.pageHeight:
			spaceAvailable -= self.textLength(self.more)
		if lineLength == 0 or (1+length) <= spaceAvailable:
			self.feedOnelinerSymbols( symbols )
		else:
			self.nextLine()
			self.feedOnelinerSymbols( symbols )
